<script setup>
import {reactive, provide} from 'vue';
const props = defineProps({
    // 表单规则
    rules: {
        default: () => null,
        type: Object
    }
})
const formItems = reactive([]);
// 验证某个字段
const validateField = (item) => {
    return new Promise((resolve, reject) => {
        const {name, value, rule, options} = item;
        if(item.rule){
            // 判断是否为必填项
            if(rule.required && !value){
                options.notifyWarn(rule.message);
                reject({name, message: rule.message});
                return ;
            // 判断是否为正则
            }else if(rule.validator instanceof RegExp){
                if(!rule.validator.test(value)){
                    options.notifyWarn(rule.message);
                    reject({name, message: rule.message});
                    return ;
                }
            // 判断是否为方法
            }else if(rule.validator instanceof Function){
                const result = rule.validator(value);
                if(!(result && result.success)){
                    options.notifyWarn(result.message || rule.message);
                    reject({name, message: rule.message});
                    return ;
                }
            }
        }
        resolve(item);
    })
}
// 提交方法
const submit = () => {
    return new Promise((resolve, reject) => {
        const formData = {};
        formItems.forEach(item => {
            validateField(item).then(({name, value}) => {
                formData[name] = value;
            }).catch(e => reject(e));
        });
        resolve(formData);
    });
};
// 表单提交事件
const onSubmit = () => {
    submit();
    return false;
};
// 初始化表单项
provide('initFormItem', (name, value, options) => {
    name && formItems.push({
        name,
        value,
        options
    });
});
// 修改表单数据
provide('updateValue', (name, value) => {
    const formItem = formItems.find(item => item.name == name);
    if(formItem){
        formItem.value = value;
    }
});
// 删除表单项
provide('removeFormItem', (name) => {
    const formItemIndex = formItems.findIndex(item => item.name == name);
    formItemIndex != -1 && formItems.splice(formItemIndex, 1);
});
defineExpose({
    submit,
    validateField: (name, validate) => {
        const item = formItems.find(item => item.name === name);
        return new Promise((resolve, reject) => {
            // 判断项是否能找到
            if(!item){
                reject(null);
                return ;
            }
            // 校验当前项
            validateField(item)
                .then(res => {
                    // 判断是否有自定义校验器，有则继续校验
                    return validate && validate(res);
                })
                .then(res => {
                    // 判断是否有校验器或是验证成功
                    if(!res || res.success){
                        return resolve(item);
                    }
                    // 返回失败结果
                    const message = res.message || item.rule.message || '';
                    item.options.notifyWarn({name, message})
                    reject({name, message});
                })
                .catch(e => reject(e));       
        })
    }
});
</script>
<template>
	<form
		v-bind="$attrs"
		@submit.prevent="onSubmit"
        autocomplete="off" 
        novalidate
	>
		<slot />
	</form>
</template>